#include "znx_http_args.h"


void
znx_http_args_add(znx_http_args_t *args, znx_http_arg_t *arg)
{
    znx_queue_insert_tail(&args->head, &arg->queue);
}


void
znx_http_args_set(znx_http_args_t *args, znx_http_arg_t *arg)
{
    znx_http_arg_t   *match_arg = NULL;
    znx_http_arg_t   *tmp_arg;
    znx_queue_t      *q;

    q = znx_queue_head(&args->head);
    while(q != &args->head) {
        tmp_arg = znx_queue_data(q, znx_http_arg_t, queue);
        q = q->next;

        if (args->cmp_handle(&arg->key, &tmp_arg->key) == 0) {
            if (!match_arg) {
                match_arg = tmp_arg;
                continue;
            }
            znx_queue_remove(&tmp_arg->queue);
        }
    }

    if (match_arg) {
        match_arg->val = arg->val;
        match_arg->has_delimiter = arg->has_delimiter;
        return;
    }

    znx_queue_insert_tail(&args->head, &arg->queue);
}


znx_http_arg_t *
znx_http_args_get(znx_http_args_t *args, znx_str_t *key, size_t index)
{
    znx_http_arg_t      *arg;
    znx_queue_t         *q;
    size_t              i = 0;

    if (index < 0) {
        index = 0;
    }

    q = znx_queue_head(&args->head);
    while (q != &args->head) {
        arg = znx_queue_data(q, znx_http_arg_t, queue);
        q = q->next;

        if (args->cmp_handle(key, &arg->key) == 0) {
            if (i == index) {
                return arg;
            }
            i++;
        }
    }

    return NULL;
}


void
znx_http_args_del_once(znx_http_args_t *args, znx_str_t *key)
{
    znx_http_arg_t       *arg;
    znx_queue_t             *q;

    q = znx_queue_head(&args->head);
    while (q != &args->head) {
        arg = znx_queue_data(q, znx_http_arg_t, queue);
        q = q->next;

        if (args->cmp_handle(key, &arg->key) == 0) {
            znx_queue_remove(&arg->queue);
            return;
        }
    }
}


void
znx_http_args_del(znx_http_args_t *args, znx_str_t *key)
{
    znx_http_arg_t       *arg;
    znx_queue_t             *q;

    q = znx_queue_head(&args->head);
    while (q != &args->head) {
        arg = znx_queue_data(q, znx_http_arg_t, queue);
        q = q->next;

        if (args->cmp_handle(key, &arg->key) == 0) {
            znx_queue_remove(&arg->queue);
        }
    }
}


void
znx_http_args_parse(znx_pool_t *pool, znx_http_args_t *args, znx_str_t *args_str)
{
    znx_str_t       key = ZNX_NULL_STRING;
    znx_str_t       val = ZNX_NULL_STRING;
    znx_bool_t      has_delimiter = ZNX_FALSE;
    znx_http_arg_t  *arg;

    u_char          *p, *q, *end;
    znx_bool_t      parsing_value;

    p = args_str->data;
    q = p;
    end = p + args_str->len;
    parsing_value = ZNX_FALSE;

    while (p != end) {
        if (*p == '=' && !parsing_value) {
            /* key data is between p and q */
            has_delimiter = ZNX_TRUE;
            key.data = q;
            key.len = (size_t)(p - q);
            /* skip the current '=' char */
            p++;
            q = p;
            parsing_value = ZNX_TRUE;
            continue;
        }

        if (*p == '&') {
            if (parsing_value) {
                parsing_value = ZNX_FALSE;
                val.data = q;
                val.len = (size_t)(p - q);
            } else {
                key.data = q;
                key.len = (size_t)(p - q);
                val.data = NULL;
                val.len = 0;
            }

            arg = znx_pool_calloc(pool, sizeof(znx_http_arg_t));
            arg->key = key;
            arg->val = val;
            arg->has_delimiter = has_delimiter;
            znx_http_args_add(args, arg);

            /* skip the current '&' char */
            p++;
            q = p;

            ZNX_STR_SET_NULL(&key);
            ZNX_STR_SET_NULL(&val);
            has_delimiter = ZNX_FALSE;

            continue;
        }

        p++;
    }

    if (p != q || parsing_value) {
        if (parsing_value) {
            val.data = q;
            val.len = (size_t)(p - q);
        } else {
            key.data = q;
            key.len = (size_t)(p - q);
            val.data = NULL;
            val.len = 0;
        }

        arg = znx_pool_calloc(pool, sizeof(znx_http_arg_t));
        arg->key = key;
        arg->val = val;
        arg->has_delimiter = has_delimiter;
        znx_http_args_add(args, arg);
    }
}


// real data space is apploed from the pool.
void
znx_http_args_to_string(znx_pool_t *pool, znx_http_args_t *args, znx_str_t *dst_str)
{
    size_t              len = 0;
    znx_queue_t         *q;
    znx_http_arg_t      *arg;

    q = znx_queue_head(&args->head);
    while (q != &args->head) {
        arg = znx_queue_data(q, znx_http_arg_t, queue);
        if (arg->val.len == 0 && !arg->has_delimiter) {
            // name&
            len = len + arg->key.len + 1;
        } else {
            // name=value&
            len = len + arg->key.len + 1 + arg->val.len + 1;
        }
        q = q->next;
    }

    if (len == 0) {
        dst_str->data = NULL;
        dst_str->len = 0;
        return;
    }

    u_char *str = znx_pool_calloc(pool, len);
    u_char *p = str;
    q = znx_queue_head(&args->head);
    while (q != &args->head) {
        arg = znx_queue_data(q, znx_http_arg_t, queue);
        znx_cpymem(p, arg->key.data, arg->key.len);
        p += arg->key.len;

        if (arg->val.len == 0 && !arg->has_delimiter) {
            // do nothing.
        } else {
            *p++ = '=';
            znx_cpymem(p, arg->val.data, arg->val.len);
            p += arg->val.len;
        }

        *p++ = '&';
        q = q->next;
    }

    // replease last char '&' to '\0'
    str[len - 1] = '\0';
    dst_str->data = str;
    dst_str->len = len - 1;
}

